/**
 * 组件扩展
 * 1、添加组件
 *      name：组件名称
 *      show：是否显示
 *      field：包含字段
 *      lang：语言包
 *      render：组件渲染函数
 *      update：组件更新渲染函数
 *      jsonData：生成组件数据
 *      property：组件属性渲染函数
 *      bindEvent：组件事件绑定函数
 *      scriptStr：组件脚本字符串函数
 *      cssStr：组件样式字符串函数
 * 2、加入分组
 */

layui.define(["all", "Sortable"], function (exports) {
    var $ = layui.jquery;

    //常量
    var MOD_NAME = 'formDesigner'
        , TP_MAIN = [
        '<div class="layui-layout layui-layout-admin" id="formDesigner_1631063987">'
        , '<div class="layui-header">'
        , '<div class="layui-logo">表单设计器</div>'
        , '<ul class="layui-nav layui-layout-right">'
        , '<li class="layui-nav-item">'
        , '<a id="btnImportJson" href="#" class="importJson">导入数据</a>'
        , '</li>'
        , '<li class="layui-nav-item">'
        , '<a id="btnExportJson" href="#" class="exportJson">导出数据</a>'
        , '</li>'
        , '<li class="layui-nav-item">'
        , '<a href="#" class="generateCode">生成代码</a>'
        , '</li>'
        , '<li class="layui-nav-item">'
        , '<a href="#" class="previewForm">预览效果</a>'
        , '</li>'
        , '</ul>'
        , '</div>'
        , '<div class="layui-side" style="top:{0}px;">'
        , '<div class="layui-side-scroll" style="width: 260px;">'
        , '<div class="layui-tab layui-tab-brief" lay-filter="components-list">'
        , '<ul class="layui-tab-title">'
        , '<li class="layui-this">组件</li>'
        , '</ul>'
        , '<div class="layui-tab-content">'
        , '<div class="layui-tab-item layui-show">'
        , '<div class="components-list" id="components-form-list">'
        , '</div>'
        , '</div>'
        , '<div class="layui-tab-item components-list" id="components-form-list">开发中...</div>'
        , '</div>'
        , '</div>'
        , '</div>'
        , '</div>'
        , '<div class="layui-body" style="top:{0}px;">'
        , '<form class="layui-form layui-form-pane" style="height:100%;padding:10px;box-sizing:border-box;">'
        , '<div class="layui-form" id="formDesignerForm" lay-filter="formDesignerForm">'
        , '</div>'
        , '</form>'
        , '</div>'
        , '<div class="layui-side-right" style="top:{0}px;">'
        , '<div class="layui-side-scroll" style="width: 350px;">'
        , '<!-- 属性导航 -->'
        , '<form class="layui-form  layui-form-pane">'
        , '<div class="layui-tab layui-tab-brief" lay-filter="form-attr">'
        , '<ul class="layui-tab-title">'
        , '<li class="layui-this">字段设置</li>'
        , '</ul>'
        , '<div class="layui-tab-content">'
        , '<div class="layui-tab-item layui-show" id="columnProperty">'
        , '</div>'
        , '</div>'
        , '</div>'
        , '</form>'
        , '</div>'
        , '</div>'
        , '</div>'].join('')
        , TP_HTML_VIEW = [
        '<div class="htmlcodeview layui-layer-wrap" style="display: none;">'
        , '<textarea class="site-demo-text" id="generate-code-view"></textarea>'
        , '<a href="javascript:;" class="layui-btn layui-btn-normal" style="margin-right:20px;" id="copy-html-code">复制代码</a>'
        , '</div>'
    ].join('')
        , TP_IMPORT_VIEW = [
        '<div class="importjsoncodeview layui-layer-wrap" style="display: none;">'
        , '<textarea class="site-demo-text" id="import-json-code-view"></textarea>'
        , '<a href="javascript:;" class="layui-btn layui-btn-normal" style="margin-right:20px;" id="import-json-code">导入数据</a>'
        , '</div>'
    ].join('');

    //模块
    var modules = Object.keys(layui.cache.modules);
    modules.splice(modules.indexOf(MOD_NAME), 1);
    modules.unshift('all');

    //函数
    var style_html = function (html_source, indent_size, indent_character, max_char) {
        function Parser() {
            this.pos = 0; //Parser position
            this.token = '';
            this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
            this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
                parent: 'parent1',
                parentcount: 1,
                parent1: ''
            };
            this.tag_type = '';
            this.token_text = this.last_token = this.last_text = this.token_type = '';

            this.Utils = { //Uilities made available to the various functions
                whitespace: "\n\r\t ".split(''),
                single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed'.split(','), //all the single tags for HTML
                extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them
                in_array: function (what, arr) {
                    for (var i = 0; i < arr.length; i++) {
                        if (what === arr[i]) {
                            return true;
                        }
                    }
                    return false;
                }
            };

            this.get_content = function () { //function to capture regular content between tags

                var char = '';
                var content = [];
                var space = false; //if a space is needed
                while (this.input.charAt(this.pos) !== '<') {
                    if (this.pos >= this.input.length) {
                        return content.length ? content.join('') : ['', 'TK_EOF'];
                    }

                    char = this.input.charAt(this.pos);
                    this.pos++;
                    this.line_char_count++;


                    if (this.Utils.in_array(char, this.Utils.whitespace)) {
                        if (content.length) {
                            space = true;
                        }
                        this.line_char_count--;
                        continue; //don't want to insert unnecessary space
                    } else if (space) {
                        if (this.line_char_count >= this.max_char) { //insert a line when the max_char is reached
                            content.push('\n');
                            for (var i = 0; i < this.indent_level; i++) {
                                content.push(this.indent_string);
                            }
                            this.line_char_count = 0;
                        } else {
                            content.push(' ');
                            this.line_char_count++;
                        }
                        space = false;
                    }
                    content.push(char); //letter at-a-time (or string) inserted to an array
                }
                return content.length ? content.join('') : '';
            };

            this.get_script = function () { //get the full content of a script to pass to js_beautify

                var char = '';
                var content = [];
                var reg_match = new RegExp('\<\/script' + '\>', 'igm');
                reg_match.lastIndex = this.pos;
                var reg_array = reg_match.exec(this.input);
                var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
                while (this.pos < end_script) { //get everything in between the script tags
                    if (this.pos >= this.input.length) {
                        return content.length ? content.join('') : ['', 'TK_EOF'];
                    }

                    char = this.input.charAt(this.pos);
                    this.pos++;


                    content.push(char);
                }
                return content.length ? content.join('') : ''; //we might not have any content at all
            };

            this.record_tag = function (tag) { //function to record a tag and its parent in this.tags Object
                if (this.tags[tag + 'count']) { //check for the existence of this tag type
                    this.tags[tag + 'count']++;
                    this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
                } else { //otherwise initialize this tag type
                    this.tags[tag + 'count'] = 1;
                    this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
                }
                this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
                this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
            };

            this.retrieve_tag = function (tag) { //function to retrieve the opening tag to the corresponding closer
                if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
                    var temp_parent = this.tags.parent; //check to see if it's a closable tag.
                    while (temp_parent) { //till we reach '' (the initial value);
                        if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
                            break;
                        }
                        temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
                    }
                    if (temp_parent) { //if we caught something
                        this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
                        this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
                    }
                    delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
                    delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
                    if (this.tags[tag + 'count'] == 1) {
                        delete this.tags[tag + 'count'];
                    } else {
                        this.tags[tag + 'count']--;
                    }
                }
            };

            this.get_tag = function () { //function to get a full tag and parse its type
                var char = '';
                var content = [];
                var space = false;

                do {
                    if (this.pos >= this.input.length) {
                        return content.length ? content.join('') : ['', 'TK_EOF'];
                    }

                    char = this.input.charAt(this.pos);
                    this.pos++;
                    this.line_char_count++;

                    if (this.Utils.in_array(char, this.Utils.whitespace)) { //don't want to insert unnecessary space
                        space = true;
                        this.line_char_count--;
                        continue;
                    }

                    if (char === "'" || char === '"') {
                        if (!content[1] || content[1] !== '!') { //if we're in a comment strings don't get treated specially
                            char += this.get_unformatted(char);
                            space = true;
                        }
                    }

                    if (char === '=') { //no space before =
                        space = false;
                    }

                    if (content.length && content[content.length - 1] !== '=' && char !== '>'
                        && space) { //no space after = or before >
                        if (this.line_char_count >= this.max_char) {
                            this.print_newline(false, content);
                            this.line_char_count = 0;
                        } else {
                            content.push(' ');
                            this.line_char_count++;
                        }
                        space = false;
                    }
                    content.push(char); //inserts character at-a-time (or string)
                } while (char !== '>');

                var tag_complete = content.join('');
                var tag_index;
                if (tag_complete.indexOf(' ') != -1) { //if there's whitespace, thats where the tag name ends
                    tag_index = tag_complete.indexOf(' ');
                } else { //otherwise go with the tag ending
                    tag_index = tag_complete.indexOf('>');
                }
                var tag_check = tag_complete.substring(1, tag_index).toLowerCase();
                if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
                    this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
                    this.tag_type = 'SINGLE';
                } else if (tag_check === 'script') { //for later script handling
                    this.record_tag(tag_check);
                    this.tag_type = 'SCRIPT';
                } else if (tag_check === 'style') { //for future style handling (for now it justs uses get_content)
                    this.record_tag(tag_check);
                    this.tag_type = 'STYLE';
                } else if (tag_check.charAt(0) === '!') { //peek for <!-- comment
                    if (tag_check.indexOf('[if') != -1) { //peek for <!--[if conditional comment
                        if (tag_complete.indexOf('!IE') != -1) { //this type needs a closing --> so...
                            var comment = this.get_unformatted('-->', tag_complete); //...delegate to get_unformatted
                            content.push(comment);
                        }
                        this.tag_type = 'START';
                    } else if (tag_check.indexOf('[endif') != -1) {//peek for <!--[endif end conditional comment
                        this.tag_type = 'END';
                        this.unindent();
                    } else if (tag_check.indexOf('[cdata[') != -1) { //if it's a <[cdata[ comment...
                        var comment = this.get_unformatted(']]>', tag_complete); //...delegate to get_unformatted function
                        content.push(comment);
                        this.tag_type = 'SINGLE'; //<![CDATA[ comments are treated like single tags
                    } else {
                        var comment = this.get_unformatted('-->', tag_complete);
                        content.push(comment);
                        this.tag_type = 'SINGLE';
                    }
                } else {
                    if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
                        this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
                        this.tag_type = 'END';
                    } else { //otherwise it's a start-tag
                        this.record_tag(tag_check); //push it on the tag stack
                        this.tag_type = 'START';
                    }
                    if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
                        this.print_newline(true, this.output);
                    }
                }
                return content.join(''); //returns fully formatted tag
            };

            this.get_unformatted = function (delimiter, orig_tag) { //function to return unformatted content in its entirety

                if (orig_tag && orig_tag.indexOf(delimiter) != -1) {
                    return '';
                }
                var char = '';
                var content = '';
                var space = true;
                do {


                    char = this.input.charAt(this.pos);
                    this.pos++

                    if (this.Utils.in_array(char, this.Utils.whitespace)) {
                        if (!space) {
                            this.line_char_count--;
                            continue;
                        }
                        if (char === '\n' || char === '\r') {
                            content += '\n';
                            for (var i = 0; i < this.indent_level; i++) {
                                content += this.indent_string;
                            }
                            space = false; //...and make sure other indentation is erased
                            this.line_char_count = 0;
                            continue;
                        }
                    }
                    content += char;
                    this.line_char_count++;
                    space = true;


                } while (content.indexOf(delimiter) == -1);
                return content;
            };

            this.get_token = function () { //initial handler for token-retrieval
                var token;

                if (this.last_token === 'TK_TAG_SCRIPT') { //check if we need to format javascript
                    var temp_token = this.get_script();
                    if (typeof temp_token !== 'string') {
                        return temp_token;
                    }
                    token = js_beautify(temp_token, this.indent_size, this.indent_character, this.indent_level); //call the JS Beautifier
                    return [token, 'TK_CONTENT'];
                }
                if (this.current_mode === 'CONTENT') {
                    token = this.get_content();
                    if (typeof token !== 'string') {
                        return token;
                    } else {
                        return [token, 'TK_CONTENT'];
                    }
                }

                if (this.current_mode === 'TAG') {
                    token = this.get_tag();
                    if (typeof token !== 'string') {
                        return token;
                    } else {
                        var tag_name_type = 'TK_TAG_' + this.tag_type;
                        return [token, tag_name_type];
                    }
                }
            };

            this.printer = function (js_source, indent_character, indent_size, max_char) { //handles input/output and some other printing functions

                this.input = js_source || ''; //gets the input for the Parser
                this.output = [];
                this.indent_character = indent_character || ' ';
                this.indent_string = '';
                this.indent_size = indent_size || 2;
                this.indent_level = 0;
                this.max_char = max_char || 70; //maximum amount of characters per line
                this.line_char_count = 0; //count to see if max_char was exceeded

                for (var i = 0; i < this.indent_size; i++) {
                    this.indent_string += this.indent_character;
                }

                this.print_newline = function (ignore, arr) {
                    this.line_char_count = 0;
                    if (!arr || !arr.length) {
                        return;
                    }
                    if (!ignore) { //we might want the extra line
                        while (this.Utils.in_array(arr[arr.length - 1], this.Utils.whitespace)) {
                            arr.pop();
                        }
                    }
                    arr.push('\n');
                    for (var i = 0; i < this.indent_level; i++) {
                        arr.push(this.indent_string);
                    }
                }


                this.print_token = function (text) {
                    this.output.push(text);
                }

                this.indent = function () {
                    this.indent_level++;
                }

                this.unindent = function () {
                    if (this.indent_level > 0) {
                        this.indent_level--;
                    }
                }
            };

            return this;
        }

        var multi_parser = new Parser();
        multi_parser.printer(html_source, indent_character, indent_size);

        while (true) {
            var t = multi_parser.get_token();
            multi_parser.token_text = t[0];
            multi_parser.token_type = t[1];

            if (multi_parser.token_type === 'TK_EOF') {
                break;
            }

            switch (multi_parser.token_type) {
                case 'TK_TAG_START':
                case 'TK_TAG_SCRIPT':
                case 'TK_TAG_STYLE':
                    multi_parser.print_newline(false, multi_parser.output);
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.indent();
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_TAG_END':
                    multi_parser.print_newline(true, multi_parser.output);
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_TAG_SINGLE':
                    multi_parser.print_newline(false, multi_parser.output);
                    multi_parser.print_token(multi_parser.token_text);
                    multi_parser.current_mode = 'CONTENT';
                    break;
                case 'TK_CONTENT':
                    if (multi_parser.token_text !== '') {
                        multi_parser.print_newline(false, multi_parser.output);
                        multi_parser.print_token(multi_parser.token_text);
                    }
                    multi_parser.current_mode = 'TAG';
                    break;
            }
            multi_parser.last_token = multi_parser.token_type;
            multi_parser.last_text = multi_parser.token_text;
        }

        return multi_parser.output.join('');
    }
        , js_beautify = function (js_source_text, indent_size, indent_character, indent_level) {
        var input, output, token_text, last_type, last_text, last_word, current_mode, modes, indent_string;
        var whitespace, wordchar, punct, parser_pos, line_starters, in_case;
        var prefix, token_type, do_block_just_closed, var_line, var_line_tainted;

        function trim_output() {
            while (output.length && (output[output.length - 1] === ' ' || output[output.length - 1] === indent_string)) {
                output.pop();
            }
        }

        function print_newline(ignore_repeated) {
            ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;

            trim_output();

            if (!output.length) {
                return; //no newline on start of file
            }

            if (output[output.length - 1] !== "\n" || !ignore_repeated) {
                output.push("\n");
            }
            for (var i = 0; i < indent_level; i++) {
                output.push(indent_string);
            }
        }

        function print_space() {
            var last_output = output.length ? output[output.length - 1] : ' ';
            if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { //prevent occassional duplicate space
                output.push(' ');
            }
        }

        function print_token() {
            output.push(token_text);
        }

        function indent() {
            indent_level++;
        }

        function unindent() {
            if (indent_level) {
                indent_level--;
            }
        }

        function remove_indent() {
            if (output.length && output[output.length - 1] === indent_string) {
                output.pop();
            }
        }

        function set_mode(mode) {
            modes.push(current_mode);
            current_mode = mode;
        }

        function restore_mode() {
            do_block_just_closed = current_mode === 'DO_BLOCK';
            current_mode = modes.pop();
        }

        function in_array(what, arr) {
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] === what) {
                    return true;
                }
            }
            return false;
        }

        function get_next_token() {
            var n_newlines = 0;
            var c = '';

            do {
                if (parser_pos >= input.length) {
                    return ['', 'TK_EOF'];
                }
                c = input.charAt(parser_pos);

                parser_pos += 1;
                if (c === "\n") {
                    n_newlines += 1;
                }
            }
            while (in_array(c, whitespace));

            if (n_newlines > 1) {
                for (var i = 0; i < 2; i++) {
                    print_newline(i === 0);
                }
            }
            var wanted_newline = (n_newlines === 1);


            if (in_array(c, wordchar)) {
                if (parser_pos < input.length) {
                    while (in_array(input.charAt(parser_pos), wordchar)) {
                        c += input.charAt(parser_pos);
                        parser_pos += 1;
                        if (parser_pos === input.length) {
                            break;
                        }
                    }
                }

                //small and surprisingly unugly hack for 1E-10 representation
                if (parser_pos !== input.length && c.match(/^[0-9]+[Ee]$/) && input.charAt(parser_pos) === '-') {
                    parser_pos += 1;

                    var t = get_next_token(parser_pos);
                    c += '-' + t[0];
                    return [c, 'TK_WORD'];
                }

                if (c === 'in') { //hack for 'in' operator
                    return [c, 'TK_OPERATOR'];
                }
                return [c, 'TK_WORD'];
            }

            if (c === '(' || c === '[') {
                return [c, 'TK_START_EXPR'];
            }

            if (c === ')' || c === ']') {
                return [c, 'TK_END_EXPR'];
            }

            if (c === '{') {
                return [c, 'TK_START_BLOCK'];
            }

            if (c === '}') {
                return [c, 'TK_END_BLOCK'];
            }

            if (c === ';') {
                return [c, 'TK_END_COMMAND'];
            }

            if (c === '/') {
                var comment = '';
                //peek for comment /* ... */
                if (input.charAt(parser_pos) === '*') {
                    parser_pos += 1;
                    if (parser_pos < input.length) {
                        while (!(input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input.length) {
                            comment += input.charAt(parser_pos);
                            parser_pos += 1;
                            if (parser_pos >= input.length) {
                                break;
                            }
                        }
                    }
                    parser_pos += 2;
                    return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
                }
                //peek for comment //...
                if (input.charAt(parser_pos) === '/') {
                    comment = c;
                    while (input.charAt(parser_pos) !== "\x0d" && input.charAt(parser_pos) !== "\x0a") {
                        comment += input.charAt(parser_pos);
                        parser_pos += 1;
                        if (parser_pos >= input.length) {
                            break;
                        }
                    }
                    parser_pos += 1;
                    if (wanted_newline) {
                        print_newline();
                    }
                    return [comment, 'TK_COMMENT'];
                }

            }

            if (c === "'" || //string
                c === '"' || //string
                (c === '/' &&
                    ((last_type === 'TK_WORD' && last_text === 'return') || (last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EOF' || last_type === 'TK_END_COMMAND')))) { //regexp
                var sep = c;
                var esc = false;
                c = '';

                if (parser_pos < input.length) {

                    while (esc || input.charAt(parser_pos) !== sep) {
                        c += input.charAt(parser_pos);
                        if (!esc) {
                            esc = input.charAt(parser_pos) === '\\';
                        } else {
                            esc = false;
                        }
                        parser_pos += 1;
                        if (parser_pos >= input.length) {
                            break;
                        }
                    }

                }

                parser_pos += 1;
                if (last_type === 'TK_END_COMMAND') {
                    print_newline();
                }
                return [sep + c + sep, 'TK_STRING'];
            }

            if (in_array(c, punct)) {
                while (parser_pos < input.length && in_array(c + input.charAt(parser_pos), punct)) {
                    c += input.charAt(parser_pos);
                    parser_pos += 1;
                    if (parser_pos >= input.length) {
                        break;
                    }
                }
                return [c, 'TK_OPERATOR'];
            }

            return [c, 'TK_UNKNOWN'];
        }

        indent_character = indent_character || ' ';
        indent_size = indent_size || 4;

        indent_string = '';
        while (indent_size--) {
            indent_string += indent_character;
        }

        input = js_source_text;

        last_word = ''; //last 'TK_WORD' passed
        last_type = 'TK_START_EXPR'; //last token type
        last_text = ''; //last token text
        output = [];

        do_block_just_closed = false;
        var_line = false;
        var_line_tainted = false;

        whitespace = "\n\r\t ".split('');
        wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
        punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |='.split(' ');

        //words which should always start on new line.
        line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',');

        //states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'.
        //some formatting depends on that.
        current_mode = 'BLOCK';
        modes = [current_mode];

        indent_level = indent_level || 0;
        parser_pos = 0; //parser position
        in_case = false; //flag for parser that case/default has been processed, and next colon needs special attention
        while (true) {
            var t = get_next_token(parser_pos);
            token_text = t[0];
            token_type = t[1];
            if (token_type === 'TK_EOF') {
                break;
            }

            switch (token_type) {

                case 'TK_START_EXPR':
                    var_line = false;
                    set_mode('EXPRESSION');
                    if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR') {
                        //do nothing on (( and )( and ][ and ]( ..
                    } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
                        print_space();
                    } else if (in_array(last_word, line_starters) && last_word !== 'function') {
                        print_space();
                    }
                    print_token();
                    break;

                case 'TK_END_EXPR':
                    print_token();
                    restore_mode();
                    break;

                case 'TK_START_BLOCK':

                    if (last_word === 'do') {
                        set_mode('DO_BLOCK');
                    } else {
                        set_mode('BLOCK');
                    }
                    if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
                        if (last_type === 'TK_START_BLOCK') {
                            print_newline();
                        } else {
                            print_space();
                        }
                    }
                    print_token();
                    indent();
                    break;

                case 'TK_END_BLOCK':
                    if (last_type === 'TK_START_BLOCK') {
                        //nothing
                        trim_output();
                        unindent();
                    } else {
                        unindent();
                        print_newline();
                    }
                    print_token();
                    restore_mode();
                    break;

                case 'TK_WORD':

                    if (do_block_just_closed) {
                        print_space();
                        print_token();
                        print_space();
                        break;
                    }

                    if (token_text === 'case' || token_text === 'default') {
                        if (last_text === ':') {
                            //switch cases following one another
                            remove_indent();
                        } else {
                            //case statement starts in the same line where switch
                            unindent();
                            print_newline();
                            indent();
                        }
                        print_token();
                        in_case = true;
                        break;
                    }


                    prefix = 'NONE';
                    if (last_type === 'TK_END_BLOCK') {
                        if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
                            prefix = 'NEWLINE';
                        } else {
                            prefix = 'SPACE';
                            print_space();
                        }
                    } else if (last_type === 'TK_END_COMMAND' && (current_mode === 'BLOCK' || current_mode === 'DO_BLOCK')) {
                        prefix = 'NEWLINE';
                    } else if (last_type === 'TK_END_COMMAND' && current_mode === 'EXPRESSION') {
                        prefix = 'SPACE';
                    } else if (last_type === 'TK_WORD') {
                        prefix = 'SPACE';
                    } else if (last_type === 'TK_START_BLOCK') {
                        prefix = 'NEWLINE';
                    } else if (last_type === 'TK_END_EXPR') {
                        print_space();
                        prefix = 'NEWLINE';
                    }

                    if (last_type !== 'TK_END_BLOCK' && in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
                        print_newline();
                    } else if (in_array(token_text, line_starters) || prefix === 'NEWLINE') {
                        if (last_text === 'else') {
                            //no need to force newline on else break
                            print_space();
                        } else if ((last_type === 'TK_START_EXPR' || last_text === '=') && token_text === 'function') {
                            //no need to force newline on 'function': (function
                            //DONOTHING
                        } else if (last_type === 'TK_WORD' && (last_text === 'return' || last_text === 'throw')) {
                            //no newline between 'return nnn'
                            print_space();
                        } else if (last_type !== 'TK_END_EXPR') {
                            if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') {
                                //no need to force newline on 'var': for (var x = 0...)
                                if (token_text === 'if' && last_type === 'TK_WORD' && last_word === 'else') {
                                    //no newline for } else if {
                                    print_space();
                                } else {
                                    print_newline();
                                }
                            }
                        } else {
                            if (in_array(token_text, line_starters) && last_text !== ')') {
                                print_newline();
                            }
                        }
                    } else if (prefix === 'SPACE') {
                        print_space();
                    }
                    print_token();
                    last_word = token_text;

                    if (token_text === 'var') {
                        var_line = true;
                        var_line_tainted = false;
                    }

                    break;

                case 'TK_END_COMMAND':

                    print_token();
                    var_line = false;
                    break;

                case 'TK_STRING':

                    if (last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK') {
                        print_newline();
                    } else if (last_type === 'TK_WORD') {
                        print_space();
                    }
                    print_token();
                    break;

                case 'TK_OPERATOR':

                    var start_delim = true;
                    var end_delim = true;
                    if (var_line && token_text !== ',') {
                        var_line_tainted = true;
                        if (token_text === ':') {
                            var_line = false;
                        }
                    }

                    if (token_text === ':' && in_case) {
                        print_token(); //colon really asks for separate treatment
                        print_newline();
                        break;
                    }

                    in_case = false;

                    if (token_text === ',') {
                        if (var_line) {
                            if (var_line_tainted) {
                                print_token();
                                print_newline();
                                var_line_tainted = false;
                            } else {
                                print_token();
                                print_space();
                            }
                        } else if (last_type === 'TK_END_BLOCK') {
                            print_token();
                            print_newline();
                        } else {
                            if (current_mode === 'BLOCK') {
                                print_token();
                                print_newline();
                            } else {
                                //EXPR od DO_BLOCK
                                print_token();
                                print_space();
                            }
                        }
                        break;
                    } else if (token_text === '--' || token_text === '++') { //unary operators special case
                        if (last_text === ';') {
                            //space for (;; ++i)
                            start_delim = true;
                            end_delim = false;
                        } else {
                            start_delim = false;
                            end_delim = false;
                        }
                    } else if (token_text === '!' && last_type === 'TK_START_EXPR') {
                        //special case handling: if (!a)
                        start_delim = false;
                        end_delim = false;
                    } else if (last_type === 'TK_OPERATOR') {
                        start_delim = false;
                        end_delim = false;
                    } else if (last_type === 'TK_END_EXPR') {
                        start_delim = true;
                        end_delim = true;
                    } else if (token_text === '.') {
                        //decimal digits or object.property
                        start_delim = false;
                        end_delim = false;

                    } else if (token_text === ':') {
                        //zz: xx
                        //can't differentiate ternary op, so for now it's a ? b: c; without space before colon
                        if (last_text.match(/^\d+$/)) {
                            //a little help for ternary a ? 1 : 0;
                            start_delim = true;
                        } else {
                            start_delim = false;
                        }
                    }
                    if (start_delim) {
                        print_space();
                    }

                    print_token();

                    if (end_delim) {
                        print_space();
                    }
                    break;

                case 'TK_BLOCK_COMMENT':

                    print_newline();
                    print_token();
                    print_newline();
                    break;

                case 'TK_COMMENT':

                    //print_newline();
                    print_space();
                    print_token();
                    print_newline();
                    break;

                case 'TK_UNKNOWN':
                    print_token();
                    break;
            }

            last_type = token_type;
            last_text = token_text;
        }

        return output.join('');
    }
        , formatOption = function (json, key, value, curr_key) {
        var options = [];
        var currArr = curr_key.split(',');
        var defaultOption = false;
        for (var i = 0; i < json.length; i++) {
            if (json[i][key] && json[i][value]) {
                var tmpOption = {};
                json[i][key] += '';
                tmpOption['value'] = json[i][key];
                tmpOption['text'] = json[i][value];

                if (currArr.indexOf(json[i][key]) >= 0) {
                    defaultOption = true;
                    tmpOption['checked'] = true;
                } else {
                    tmpOption['checked'] = false;
                }
                options.push(tmpOption);
            }
        }

        return options;
    }
        , layuiPath = function (is_dir, is_relative) {
        var jsPath = document.currentScript ? document.currentScript.src : function () {
            var js = document.scripts
                , len = js.length
                , src;
            for (var i = 0; i < len; i++) {
                if (js[i].src && js[i].src.indexOf('layui.js') >= 0) {
                    src = js[i].src;
                    break;
                }
            }
            return src || js[len - 1].src;
        }();
        if (is_dir) {
            jsPath = jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
        }
        if (is_relative) {
            var href = location.href.substring(0, location.href.lastIndexOf('/'));
            jsPath = './' + jsPath.substring(href.length + 1);
        }
        return jsPath;
    }
        , styleInject = function (css, elem) {
        if (!css || typeof document === 'undefined') {
            return;
        }

        var head = elem ? elem : document.getElementsByTagName('head')[0];
        var style = document.createElement('style');
        style.type = 'text/css';

        if (head.firstChild) {
            head.insertBefore(style, head.firstChild);
        } else {
            head.appendChild(style);
        }

        if (style.styleSheet) {
            style.styleSheet.cssText = css;
        } else {
            style.appendChild(document.createTextNode(css));
        }
    };

    //加载样式
    var css = '#formDesigner_1631063987{position:relative;width:100%;height:100%}#formDesigner_1631063987 .layui-logo{color:#fff;font-size:20px}#formDesigner_1631063987 .layui-header{z-index:0;position:absolute}#formDesigner_1631063987 .layui-side{position:absolute;top:0;width:260px;overflow-x:hidden;background-color:#fff}#formDesigner_1631063987 .layui-side-right{position:absolute;right:0;top:0;bottom:0;z-index:999;width:350px;overflow-x:hidden;background-color:#fff}#formDesigner_1631063987 .layui-body{position:absolute;left:260px;right:350px;top:0;bottom:0;z-index:998;width:auto;overflow-y:auto;box-sizing:border-box;min-width:250px;border-left:1px solid #e6e6e6;border-right:1px solid #e6e6e6;background-color:#fff;padding-bottom:0;box-sizing:border-box}#formDesigner_1631063987 .components-list{padding:4px;box-sizing:border-box;}#formDesigner_1631063987 .components-list .components-item{display:inline-block;width:49%;margin:0.5%;margin-top:5px;-webkit-transition:-webkit-transform 0ms !important;transition:-webkit-transform 0ms !important;transition:transform 0ms !important;transition:transform 0ms,-webkit-transform 0ms !important}#formDesigner_1631063987 .components-draggable{padding-bottom:20px}#formDesigner_1631063987 .components-title{font-size:14px;color:#222;margin:6px 2px;font-weight:bold}#formDesigner_1631063987 .components-body{padding:8px 10px;background:#f6f7ff;font-size:12px;cursor:move;border:1px dashed #f6f7ff;border-radius:3px}#formDesigner_1631063987 .components-body:hover{border:1px dashed #787be8;color:#787be8}#formDesignerForm{background:#fff;border:1px dashed #999;min-height:100%;padding:10px 10px;box-sizing:border-box}#formDesignerForm .layui-form-item{position:relative;border:1px solid rgba(0,0,0,0)}#formDesignerForm .layui-form-item:hover{border:1px solid #409EFF;background-color:#e9f4fd !important;cursor:pointer}#formDesignerForm .ghost{border:1px dashed #e06c1d;outline-width:0;height:39px;box-sizing:border-box;font-size:0;content:\'\';overflow:hidden;padding:0}#formDesignerForm .ghost div{background-color:#fff;color:#fff}#formDesignerForm .widget-view-drag{position:absolute;left:-1px;top:-1px;bottom:-18px;height:28px;line-height:28px;background:#409EFF;z-index:9999}#formDesignerForm .widget-view-drag i{font-size:14px;color:#fff;margin:0 5px;cursor:move}#formDesignerForm .select-option-drag{cursor:move}#formDesignerForm .select-option-delete{cursor:pointer}#formDesignerForm .widget-view-action{position:absolute;right:-1px;bottom:-1px;height:28px;line-height:28px;background:#409EFF;z-index:9999}#formDesignerForm .widget-view-action i{font-size:14px;color:#fff;margin:0 5px;cursor:pointer}#formDesignerForm .widget-view-mask{position:absolute;left:0;top:0;width:100%;height:100%;background-color:transparent;z-index:9998}.htmlcodeview,.importjsoncodeview{position:relative;display:none}.htmlcodeview textarea,.importjsoncodeview textarea{display:block;width:760px;height:560px;border:10px solid #F8F8F8;border-top-width:0;padding:10px;line-height:20px;overflow:auto;background-color:#3F3F3F;color:#eee;font-size:12px}.htmlcodeview a,.importjsoncodeview a{position:absolute;right:20px;bottom:20px}';

    styleInject(css);

    //类方法
    String.prototype.format = function (args) {
        var result = this;
        if (arguments.length > 0) {
            if (arguments.length == 1 && typeof (args) == "object") {
                for (var key in args) {
                    if (args[key] != undefined) {
                        var reg = new RegExp("({" + key + "})", "g");
                        result = result.replace(reg, args[key]);
                    }
                }
            } else {
                for (var i = 0; i < arguments.length; i++) {
                    if (arguments[i] != undefined) {
                        var reg = new RegExp("({[" + i + "]})", "g");
                        result = result.replace(reg, arguments[i]);
                    }
                }
            }
        }
        return result;
    };

    //外部接口
    var Class = function (options) {
        var that = this;
        that.config = $.extend({}, that.config, options);
    };

    //默认配置
    Class.prototype.config = {
        elem: ""
        , data: []
        , val: {}
        , showHeader: true
        , callback: null
        , selectItem: null
        , componentCode: {
            html: ''
            , css: ''
            , script: ''
        }
    };

    //组件定义
    Class.prototype.components = {
        input: {
            name: '单行输入框'
            , show: true
            , field: {
                id: '-1',
                field: "input",
                tag: "input",
                label: "单行文本",
                defaultValue: "",
                readonly: false,
                required: false,
                expression: "",
            }
            , lang: {
                field: "字段",
                label: "标签",
                readonly: "只读",
                disabled: "禁用",
                required: "必填",
                expression: "验证",
            }
            , render: function (json, pack, mask) {
                var _readonly = json.readonly ? 'readonly' : '';
                var _required = json.required ? 'required' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _expression = _required + '|' + (json.expression ? json.expression : '');
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block">';

                _html += '<input name="{0}" value="{1}" placeholder="请输入{2}" class="layui-input" lay-vertype="tips" lay-verify="{3}" {4} {5}>'.format(json.field, _defaultValue, json.label, _expression, _readonly, _required);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'placeholder':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'readonly':
                        case 'required':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'readonly':
                                    yes = "只读";
                                    no = "可写";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //下拉框
                        case 'expression':
                            var options = [];
                            var currOption = '';

                            switch (key) {
                                case 'expression':
                                    options = [
                                        {text: '默认', value: ""}
                                        , {text: '手机', value: 'phone'}
                                        , {text: '邮箱', value: 'email'}
                                        , {text: '网址', value: 'url'}
                                        , {text: '数字', value: 'number'}
                                        , {text: '日期', value: 'date'}
                                        , {text: '身份证', value: 'identity'}
                                    ];
                                    currOption = json.expression;
                                    break;
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '<select name="{0}" lay-verify="required">'.format(key);
                            for (var i = 0; i < options.length; i++) {
                                if (options[i].value === currOption) {
                                    _html += '<option value="{0}" selected>{1}</option>'.format(options[i].value, options[i].text);
                                } else {
                                    _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                                }
                            }
                            _html += '</select>';
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }

                    layui.form.render();
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //下拉框
                layui.form.on('select', function (data) {
                    var _key = data.elem.name;
                    var _value = data.value;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        textarea: {
            name: '多行输入框'
            , show: true
            , field: {
                id: '-1',
                field: "textarea",
                tag: "textarea",
                label: "多行文本",
                defaultValue: "",
                readonly: false,
                required: false,
            }
            , lang: {
                field: "字段",
                label: "标签",
                readonly: "只读",
                required: "必填",
            }
            , render: function (json, pack, mask) {
                var _readonly = json.readonly ? 'readonly' : '';
                var _required = json.required ? 'required' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    var _html = '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block">';

                _html += '<textarea name="{0}" placeholder="请输入{2}" class="layui-textarea" {3} {4}>{1}</textarea>'.format(json.field, _defaultValue, json.label, _required, _readonly);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'placeholder':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'readonly':
                        case 'required':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'readonly':
                                    yes = "只读";
                                    no = "可写";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        select: {
            name: '下拉选择框'
            , show: true
            , field: {
                id: '-1',
                field: "select",
                tag: "select",
                label: "下拉框",
                defaultValue: "",
                disabled: false,
                required: false,
                datasourceType: 'local',
                remoteUrl: '',
                remoteOptionValue: 'value',
                remoteOptionText: 'text',
                options: [
                    {
                        text: 'option1',
                        value: 'value1',
                    },
                    {
                        text: 'option2',
                        value: 'value2',
                    },
                    {
                        text: 'option3',
                        value: 'value3',
                    },
                ]
            }
            , lang: {
                field: "字段",
                label: "标签",
                placeholder: "占位符",
                disabled: "禁用",
                required: "必填",
                datasourceType: '资源类型',
                remoteUrl: '接口地址',
                remoteOptionValue: '键映射',
                remoteOptionText: '值映射',
                options: '选项',
            }
            , render: function (json, pack, mask) {
                var _disabled = json.disabled ? 'disabled' : '';
                var _required = json.required ? 'required' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html = '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block layui-form" lay-filter="{0}">'.format(json.id);

                _html += '<select name="{0}" lay-verify="{2}" {1}>'.format(json.field, _disabled, _required);

                var options = [];

                //远程
                if (json.datasourceType == 'remote') {
                    if (json.remoteUrl) {
                        $.ajax({
                            type: 'get'
                            , url: json.remoteUrl
                            , async: false
                            , success: function (res) {
                                res = JSON.parse(JSON.stringify(res));
                                if (res && res.code == 0) {
                                    options = formatOption(res.data, json.remoteOptionValue, json.remoteOptionText, _defaultValue);
                                }
                            }
                        });
                    }
                } else {
                    options = json.options;
                }

                if (options) {
                    for (var i = 0; i < options.length; i++) {
                        if (options[i].checked) {
                            _html += '<option value="{0}" selected="">{1}</option>'.format(options[i].value, options[i].text);
                        } else {
                            _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                        }
                    }
                }

                _html += '</select>';

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                layui.form.render();
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;
                var that = this;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'placeholder':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'readonly':
                        case 'disabled':
                        case 'required':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'readonly':
                                    yes = "只读";
                                    no = "可写";
                                    break;
                                case 'disabled':
                                    yes = "启用";
                                    no = "禁用";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //下拉框
                        case 'datasourceType':
                            var options = [];
                            var currOption = '';

                            switch (key) {
                                case 'datasourceType':
                                    options = [
                                        {text: '本地', value: "local", checked: true}
                                        , {text: '远程', value: 'remote'}
                                    ];
                                    currOption = json.datasourceType;
                                    break;
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '<select name="{0}" lay-verify="required">'.format(key);
                            for (var i = 0; i < options.length; i++) {
                                if (options[i].value === currOption) {
                                    _html += '<option value="{0}" selected>{1}</option>'.format(options[i].value, options[i].text);
                                } else {
                                    _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                                }
                            }
                            _html += '</select>';
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //选项
                        case 'options':
                            if (json['datasourceType'] == 'local') {
                                _html += '<div class="layui-form-item" >';
                                _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                                _html += '  <div class="layui-input-block">';
                                _html += '    <button style="margin: 5px 0px 0px 30px;" type="button" id="select-option-add" class="layui-btn layui-btn-primary layui-btn-sm"><i class="layui-icon layui-icon-addition"></i>增加选项</button>';
                                _html += '  </div>';
                                _html += '</div>';
                                _html += '<div id="{0}">'.format(json.tag);
                                //选项
                                for (var i = 0; i < json.options.length; i++) {
                                    _html += '<div class="layui-form-item option select-options" data-index="{0}">'.format(i);
                                    _html += '  <div class="layui-inline" style="width: 30px; margin-right: 0px;">';

                                    if (json.options[i].checked) {
                                        _html += '   <input type="radio" name="{0}"  checked>'.format(json.tag);
                                    } else {
                                        _html += '   <input type="radio" name="{0}">'.format(json.tag);
                                    }

                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;width:110px;">';
                                    _html += '   <input type="text" name="{0}-text"  autocomplete="off" value="{1}" class="layui-input">'.format(json.tag, json.options[i].text);
                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;width:110px;">';
                                    _html += '   <input type="text" name="{0}-value"  autocomplete="off" value="{1}" class="layui-input">'.format(json.tag, json.options[i].value);
                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;">';
                                    _html += '   <i class="layui-icon layui-icon-slider select-option-drag" style="color:blue;font-size:20px;"></i>';
                                    _html += '   <i class="layui-icon layui-icon-delete select-option-delete" style="color:red;font-size:20px;"></i>';
                                    _html += '  </div>';
                                    _html += '</div>';
                                }
                                _html += '</div>';
                            }
                            break;

                        //远程
                        case 'remoteUrl':
                        case 'remoteOptionText':
                        case 'remoteOptionValue':
                            if (json['datasourceType'] == 'remote') {
                                _html += '<div class="layui-form-item" >';
                                _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                                _html += '  <div class="layui-input-block">';
                                _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                                _html += '  </div>';
                                _html += '</div>';
                            }
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
                layui.form.render();

                //排序事件
                if (json.datasourceType == 'local') {
                    var sortable = layui.Sortable.create(document.getElementById(json.tag), {
                        group: {
                            name: 'propertygroup',
                        },
                        ghostClass: "ghost",
                        handle: '.select-option-drag',
                        dataIdAttr: 'data-index',
                        animation: 150,
                        onEnd: function (evt) {
                            var indexArray = sortable.toArray();
                            var newJson = [];
                            for (var i = 0; i < indexArray.length; i++) {
                                newJson.push(json.options[indexArray[i]]);
                            }
                            json.options = newJson;
                            that.update(json);
                            that.property(json);
                            layui.form.render();
                        }
                    });
                }
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        case 'select-text':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['text'] = _value;
                            that.update(json);
                            break;
                        case 'select-value':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['value'] = _value;
                            that.update(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //下拉框
                layui.form.on('select', function (data) {
                    var _key = data.elem.name;
                    var _value = data.value;

                    switch (_key) {
                        case 'datasourceType':
                            json[_key] = _value;
                            that.update(json);
                            that.property(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //添加选项
                $(document).off('click', '#columnProperty #select-option-add').on('click', '#columnProperty #select-option-add', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    //添加html
                    json.options.splice(json.options.length + 1, 0, {
                        text: 'option',
                        value: 'value',
                        checked: false
                    });
                    that.update(json);
                    that.property(json);
                });

                //删除选项
                $(document).off('click', '#columnProperty .select-option-delete').on('click', '#columnProperty .select-option-delete', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    //从数据源 options.data 中删除节点
                    if (json.options.length <= 1) {
                        layui.layer.msg('已达到最低选项，不能继续删除');
                        return;
                    }
                    var _index = $(this).closest('.layui-form-item')[0].dataset.index;
                    if (_index !== undefined) {
                        json.options.splice(_index, 1);//删除此节点
                    }
                    var checkedDefual = true;
                    for (var i = 0; i < json.options.length; i++) {
                        if (json.options[i].checked) {
                            vv = false
                        }
                    }
                    if (checkedDefual) {
                        json.options[0].checked = true;
                    }
                    that.update(json);
                    that.property(json);
                });

                //单选框
                layui.form.on('radio', function (data) {
                    var _index = parseInt(data.elem.closest('.layui-form-item').dataset.index);
                    for (var i = 0; i < json.options.length; i++) {
                        if (i == _index) {
                            json.options[i].checked = true;
                        } else {
                            json.options[i].checked = false;
                        }
                    }
                    that.update(json);
                });
            }
            , scriptStr: function (item) {
                return [
                    'layui.form.render();'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        switch: {
            name: '开关'
            , show: true
            , field: {
                id: '-1',
                field: 'switch',
                label: "开关",
                tag: "switch",
                defaultValue: "",
                yesValue: "ON",
                noValue: "OFF",
                disabled: false,
                required: false,
            }
            , lang: {
                field: "字段",
                label: "标签",
                yesValue: "YES",
                noValue: "NO",
                disabled: "禁用",
                required: "必填",
            }
            , render: function (json, pack, mask) {
                var _disabled = json.disabled ? 'disabled=""' : '';
                var _disabledClass = json.disabled ? ' layui-disabled' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _defaultValue = (json.val[json.field] && parseInt(json.val[json.field])) ? 'checked' : '';

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block" style="border: 1px solid #d2d2d2;border-left: 0px;">';

                _html += '<input type="checkbox" name="{0}" lay-skin="switch" value="1" lay-text="{4}|{5}" {1} class="{2}" {3}>'.format(json.field, _disabled, _disabledClass, _defaultValue, json.yesValue, json.noValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                layui.form.render();
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'yesValue':
                        case 'noValue':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'disabled':
                        case 'required':
                        case 'switchValue':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'disabled':
                                    yes = "启用";
                                    no = "禁用";
                                    break;
                                case 'switchValue':
                                    yes = "ON";
                                    no = "OFF";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [
                    'layui.form.render();'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        radio: {
            name: '单选框'
            , show: true
            , field: {
                id: '-1',
                field: "radio",
                label: "单选框",
                tag: "radio",
                disabled: false,
                required: false,
                defaultValue: "",
                datasourceType: 'local',
                remoteUrl: '',
                remoteOptionValue: 'value',
                remoteOptionText: 'text',
                options: [
                    {
                        text: 'option1',
                        value: 'value1',
                    },
                    {
                        text: 'option2',
                        value: 'value2',
                    },
                    {
                        text: 'option3',
                        value: 'value3',
                    },
                ]
            }
            , lang: {
                field: "字段",
                label: "标签",
                disabled: "禁用",
                required: "必填",
                datasourceType: '资源类型',
                remoteUrl: '接口地址',
                remoteOptionValue: '键映射',
                remoteOptionText: '值映射',
                options: '选项',
            }
            , render: function (json, pack, mask) {
                var _disabled = json.disabled ? 'disabled=""' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block">';

                var options = [];

                //远程
                if (json.datasourceType == 'remote') {
                    if (json.remoteUrl) {
                        $.ajax({
                            type: 'get'
                            , url: json.remoteUrl
                            , async: false
                            , success: function (res) {
                                res = JSON.parse(JSON.stringify(res));
                                if (res && res.code == 0) {
                                    options = formatOption(res.data, json.remoteOptionValue, json.remoteOptionText, _defaultValue);
                                }
                            }
                        });
                    }
                } else {
                    options = json.options;
                }

                if (options) {
                    for (var i = 0; i < options.length; i++) {
                        if (options[i].checked) {
                            _html += '<input type="radio" name="{0}" value="{1}" title="{2}" checked {3}>'.format(json.field, options[i].value, options[i].text, _disabled);
                        } else {
                            _html += '<input type="radio" name="{0}" value="{1}" title="{2}" {3}>'.format(json.field, options[i].value, options[i].text, _disabled);
                        }
                    }
                }

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                layui.form.render();
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;
                var that = this;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'disabled':
                        case 'required':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'disabled':
                                    yes = "启用";
                                    no = "禁用";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //下拉框
                        case 'datasourceType':
                            var options = [];
                            var currOption = '';

                            switch (key) {
                                case 'datasourceType':
                                    options = [
                                        {text: '本地', value: "local", checked: true}
                                        , {text: '远程', value: 'remote'}
                                    ];
                                    currOption = json.datasourceType;
                                    break;
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '<select name="{0}" lay-verify="required">'.format(key);
                            for (var i = 0; i < options.length; i++) {
                                if (options[i].value === currOption) {
                                    _html += '<option value="{0}" selected>{1}</option>'.format(options[i].value, options[i].text);
                                } else {
                                    _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                                }
                            }
                            _html += '</select>';
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //选项
                        case 'options':
                            if (json['datasourceType'] == 'local') {
                                _html += '<div class="layui-form-item" >';
                                _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                                _html += '  <div class="layui-input-block">';
                                _html += '    <button style="margin: 5px 0px 0px 30px;" type="button" id="select-option-add" class="layui-btn layui-btn-primary layui-btn-sm"><i class="layui-icon layui-icon-addition"></i>增加选项</button>';
                                _html += '  </div>';
                                _html += '</div>';
                                _html += '<div id="{0}">'.format(json.tag);
                                //选项
                                for (var i = 0; i < json.options.length; i++) {
                                    _html += '<div class="layui-form-item option select-options" data-index="{0}">'.format(i);
                                    _html += '  <div class="layui-inline" style="width: 30px; margin-right: 0px;">';

                                    if (json.options[i].checked) {
                                        _html += '   <input type="radio" name="{0}"  checked>'.format(json.tag);
                                    } else {
                                        _html += '   <input type="radio" name="{0}">'.format(json.tag);
                                    }

                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;width:110px;">';
                                    _html += '   <input type="text" name="{0}-text"  autocomplete="off" value="{1}" class="layui-input">'.format(json.tag, json.options[i].text);
                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;width:110px;">';
                                    _html += '   <input type="text" name="{0}-value"  autocomplete="off" value="{1}" class="layui-input">'.format(json.tag, json.options[i].value);
                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;">';
                                    _html += '   <i class="layui-icon layui-icon-slider select-option-drag" style="color:blue;font-size:20px;"></i>';
                                    _html += '   <i class="layui-icon layui-icon-delete select-option-delete" style="color:red;font-size:20px;"></i>';
                                    _html += '  </div>';
                                    _html += '</div>';
                                }
                                _html += '</div>';
                            }
                            break;

                        //远程
                        case 'remoteUrl':
                        case 'remoteOptionText':
                        case 'remoteOptionValue':
                            if (json['datasourceType'] == 'remote') {
                                _html += '<div class="layui-form-item" >';
                                _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                                _html += '  <div class="layui-input-block">';
                                _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                                _html += '  </div>';
                                _html += '</div>';
                            }
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
                layui.form.render();

                //排序事件
                if (json.datasourceType == 'local') {
                    var sortable = layui.Sortable.create(document.getElementById(json.tag), {
                        group: {
                            name: 'propertygroup',
                        },
                        ghostClass: "ghost",
                        handle: '.select-option-drag',
                        dataIdAttr: 'data-index',
                        animation: 150,
                        onEnd: function (evt) {
                            var indexArray = sortable.toArray();
                            var newJson = [];
                            for (var i = 0; i < indexArray.length; i++) {
                                newJson.push(json.options[indexArray[i]]);
                            }
                            json.options = newJson;
                            that.update(json);
                            that.property(json);
                            layui.form.render();
                        }
                    });
                }
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        case 'radio-text':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['text'] = _value;
                            that.update(json);
                            break;
                        case 'radio-value':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['value'] = _value;
                            that.update(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //下拉框
                layui.form.on('select', function (data) {
                    var _key = data.elem.name;
                    var _value = data.value;

                    switch (_key) {
                        case 'datasourceType':
                            json[_key] = _value;
                            that.update(json);
                            that.property(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //添加选项
                $(document).off('click', '#columnProperty #select-option-add').on('click', '#columnProperty #select-option-add', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    //添加html
                    json.options.splice(json.options.length + 1, 0, {
                        text: 'option',
                        value: 'value',
                        checked: false
                    });
                    that.update(json);
                    that.property(json);
                });

                //删除选项
                $(document).off('click', '#columnProperty .select-option-delete').on('click', '#columnProperty .select-option-delete', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    //从数据源 options.data 中删除节点
                    if (json.options.length <= 1) {
                        layui.layer.msg('已达到最低选项，不能继续删除');
                        return;
                    }
                    var _index = $(this).closest('.layui-form-item')[0].dataset.index;
                    if (_index !== undefined) {
                        json.options.splice(_index, 1);//删除此节点
                    }
                    var checkedDefual = true;
                    for (var i = 0; i < json.options.length; i++) {
                        if (json.options[i].checked) {
                            vv = false
                        }
                    }
                    if (checkedDefual) {
                        json.options[0].checked = true;
                    }
                    that.update(json);
                    that.property(json);
                });

                //单选框
                layui.form.on('radio', function (data) {
                    var _index = parseInt(data.elem.closest('.layui-form-item').dataset.index);
                    for (var i = 0; i < json.options.length; i++) {
                        if (i == _index) {
                            json.options[i].checked = true;
                        } else {
                            json.options[i].checked = false;
                        }
                    }
                    that.update(json);
                });
            }
            , scriptStr: function (item) {
                return [
                    'layui.form.render();'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        checkbox: {
            name: '多选框'
            , show: true
            , field: {
                id: '-1',
                field: 'checkbox',
                label: "多选框",
                tag: "checkbox",
                disabled: false,
                required: false,
                defaultValue: '',
                datasourceType: 'local',
                remoteUrl: '',
                remoteOptionValue: 'value',
                remoteOptionText: 'text',
                options: [
                    {
                        text: 'option1',
                        value: 'value1',
                    },
                    {
                        text: 'option2',
                        value: 'value2',
                    },
                    {
                        text: 'option3',
                        value: 'value3',
                    },
                ]
            }
            , lang: {
                field: "字段",
                label: "标签",
                disabled: "禁用",
                required: "必填",
                datasourceType: '资源类型',
                remoteUrl: '接口地址',
                remoteOptionValue: '键映射',
                remoteOptionText: '值映射',
                options: '选项',
            }
            , render: function (json, pack, mask) {
                var _disabled = json.disabled ? 'disabled' : '';
                var _required = json.required ? 'required' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block">';

                var options = [];

                //远程
                if (json.datasourceType == 'remote') {
                    if (json.remoteUrl) {
                        $.ajax({
                            type: 'get'
                            , url: json.remoteUrl
                            , async: false
                            , success: function (res) {
                                res = JSON.parse(JSON.stringify(res));
                                if (res && res.code == 0) {
                                    options = formatOption(res.data, json.remoteOptionValue, json.remoteOptionText, _defaultValue);
                                }
                            }
                        });
                    }
                } else {
                    options = json.options;
                }

                if (options) {
                    for (var i = 0; i < options.length; i++) {
                        if (options[i].checked) {
                            _html += '<input type="checkbox" name="{0}[]" value="{1}" title="{2}" checked {3} {4}>'.format(json.field, options[i].value, options[i].text, _disabled, _required);
                        } else {
                            _html += '<input type="checkbox" name="{0}[]" value="{1}" title="{2}" {3} {4}>'.format(json.field, options[i].value, options[i].text, _disabled, _required);
                        }
                    }
                }

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                layui.form.render();
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;
                var that = this;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'disabled':
                        case 'required':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'disabled':
                                    yes = "启用";
                                    no = "禁用";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //下拉框
                        case 'datasourceType':
                            var options = [];
                            var currOption = '';

                            switch (key) {
                                case 'datasourceType':
                                    options = [
                                        {text: '本地', value: "local", checked: true}
                                        , {text: '远程', value: 'remote'}
                                    ];
                                    currOption = json.datasourceType;
                                    break;
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '<select name="{0}" lay-verify="required">'.format(key);
                            for (var i = 0; i < options.length; i++) {
                                if (options[i].value === currOption) {
                                    _html += '<option value="{0}" selected>{1}</option>'.format(options[i].value, options[i].text);
                                } else {
                                    _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                                }
                            }
                            _html += '</select>';
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //选项
                        case 'options':
                            if (json['datasourceType'] == 'local') {
                                _html += '<div class="layui-form-item" >';
                                _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                                _html += '  <div class="layui-input-block">';
                                _html += '    <button style="margin: 5px 0px 0px 30px;" type="button" id="select-option-add" class="layui-btn layui-btn-primary layui-btn-sm"><i class="layui-icon layui-icon-addition"></i>增加选项</button>';
                                _html += '  </div>';
                                _html += '</div>';
                                _html += '<div id="{0}">'.format(json.tag);
                                //选项
                                for (var i = 0; i < json.options.length; i++) {
                                    _html += '<div class="layui-form-item option select-options" data-index="{0}">'.format(i);
                                    _html += '  <div class="layui-inline" style="width: 30px; margin-right: 0px;">';

                                    if (json.options[i].checked) {
                                        _html += '    <input type="checkbox" name="{0}" lay-skin="primary" checked>'.format(json.tag);
                                    } else {
                                        _html += '    <input type="checkbox" name="{0}" lay-skin="primary">'.format(json.tag);
                                    }

                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;width:110px;">';
                                    _html += '   <input type="text" name="{0}-text"  autocomplete="off" value="{1}" class="layui-input">'.format(json.tag, json.options[i].text);
                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;width:110px;">';
                                    _html += '   <input type="text" name="{0}-value"  autocomplete="off" value="{1}" class="layui-input">'.format(json.tag, json.options[i].value);
                                    _html += '  </div>';
                                    _html += '  <div class="layui-inline" style="margin-right: 0px;">';
                                    _html += '   <i class="layui-icon layui-icon-slider select-option-drag" style="color:blue;font-size:20px;"></i>';
                                    _html += '   <i class="layui-icon layui-icon-delete select-option-delete" style="color:red;font-size:20px;"></i>';
                                    _html += '  </div>';
                                    _html += '</div>';
                                }
                                _html += '</div>';
                            }
                            break;

                        //远程
                        case 'remoteUrl':
                        case 'remoteOptionText':
                        case 'remoteOptionValue':
                        case 'remoteDefaultValue':
                            if (json['datasourceType'] == 'remote') {
                                _html += '<div class="layui-form-item" >';
                                _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                                _html += '  <div class="layui-input-block">';
                                _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                                _html += '  </div>';
                                _html += '</div>';
                            }
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
                layui.form.render();

                //排序事件
                if (json.datasourceType == 'local') {
                    var sortable = layui.Sortable.create(document.getElementById(json.tag), {
                        group: {
                            name: 'propertygroup',
                        },
                        ghostClass: "ghost",
                        handle: '.select-option-drag',
                        dataIdAttr: 'data-index',
                        animation: 150,
                        onEnd: function (evt) {
                            var indexArray = sortable.toArray();
                            var newJson = [];
                            for (var i = 0; i < indexArray.length; i++) {
                                newJson.push(json.options[indexArray[i]]);
                            }
                            json.options = newJson;
                            that.update(json);
                            that.property(json);
                            layui.form.render();
                        }
                    });
                }
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        case 'checkbox-text':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['text'] = _value;
                            that.update(json);
                            break;
                        case 'checkbox-value':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['value'] = _value;
                            that.update(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //下拉框
                layui.form.on('select', function (data) {
                    var _key = data.elem.name;
                    var _value = data.value;

                    switch (_key) {
                        case 'datasourceType':
                            json[_key] = _value;
                            that.update(json);
                            that.property(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //添加选项
                $(document).off('click', '#columnProperty #select-option-add').on('click', '#columnProperty #select-option-add', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    //添加html
                    json.options.splice(json.options.length + 1, 0, {
                        text: 'option',
                        value: 'value',
                        checked: false
                    });
                    that.update(json);
                    that.property(json);
                });

                //删除选项
                $(document).off('click', '#columnProperty .select-option-delete').on('click', '#columnProperty .select-option-delete', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    //从数据源 options.data 中删除节点
                    if (json.options.length <= 1) {
                        layui.layer.msg('已达到最低选项，不能继续删除');
                        return;
                    }
                    var _index = $(this).closest('.layui-form-item')[0].dataset.index;
                    if (_index !== undefined) {
                        json.options.splice(_index, 1);//删除此节点
                    }
                    var checkedDefual = true;
                    for (var i = 0; i < json.options.length; i++) {
                        if (json.options[i].checked) {
                            vv = false
                        }
                    }
                    if (checkedDefual) {
                        json.options[0].checked = true;
                    }
                    that.update(json);
                    that.property(json);
                });

                //多选框
                layui.form.on('checkbox', function (data) {
                    var _index = parseInt($("#" + json.id + " .layui-input-block div.layui-form-checkbox").index(data.othis[0]));
                    if ($(data.othis[0]).parent().parent().parent().attr("id") === 'checkbox') {
                        _index = parseInt($(data.othis[0]).parent().parent().attr("data-index"));
                    }
                    for (var i = 0; i < json.options.length; i++) {
                        if (i == _index) {
                            json.options[i].checked = data.elem.checked;
                            break;
                        }
                    }
                    that.update(json);
                    that.property(json);
                });
            }
            , scriptStr: function (item) {
                return [
                    'layui.form.render();'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        date: {
            name: '日期'
            , show: true
            , field: {
                id: '-1',
                field: 'date',
                tag: "date",
                label: "日期",
                defaultValue: "",
                disabled: false,
                required: false,
                datetype: "date",
                dateformat: "yyyy-MM-dd",
            }
            , lang: {
                field: "字段",
                label: "标签",
                disabled: "禁用",
                required: "必填",
                datetype: "日期类型",
                dateformat: "日期格式",
            }
            , render: function (json, pack, mask) {
                var _disabled = json.disabled ? 'disabled=""' : '';
                var _disabledClass = json.disabled ? ' layui-disabled' : '';
                var _disabledStyle = json.disabled ? ' pointer-events: none;' : '';
                var _required = json.required ? 'required' : '';
                var _requiredClass = json.required ? 'layui-form-required' : '';
                var _requiredStr = json.required ? '*' : '';
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label {0}"><span style="color:red;">{2}</span>{1}：</label>'.format(_requiredClass, json.label, _requiredStr);

                _html += '<div class="layui-input-block">';

                _html += '<input id="{0}" name="{4}" value="{6}" lay-verify="{3}" autocomplete="off" class="layui-input icon-date widget-date {1}" style="line-height: 40px;" {2} {5}/>'.format(json.tag + json.id, _disabledClass, _disabledStyle, _required, json.field, _disabled, _defaultValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                eval(this.scriptStr(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'disabled':
                        case 'required':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'disabled':
                                    yes = "启用";
                                    no = "禁用";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //下拉框
                        case 'datetype':
                            var options = [];
                            var currOption = '';

                            switch (key) {
                                case 'datetype':
                                    options = [
                                        {text: '年选择器', value: 'year'}
                                        , {text: '年月选择器', value: 'month'}
                                        , {text: '时间选择器', value: 'time'}
                                        , {text: '日期选择器', value: 'date'}
                                        , {text: '日期时间选择器', value: 'datetime'}
                                    ];
                                    currOption = json.datetype;
                                    break;
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '<select name="{0}" lay-verify="required">'.format(key);
                            for (var i = 0; i < options.length; i++) {
                                if (options[i].value === currOption) {
                                    _html += '<option value="{0}" selected>{1}</option>'.format(options[i].value, options[i].text);
                                } else {
                                    _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                                }
                            }
                            _html += '</select>';
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //下拉框
                        case 'dateformat':
                            var options = [];
                            var currOption = '';

                            switch (json['datetype']) {
                                case 'year':
                                    options = [
                                        {text: 'yyyy', value: 'yyyy'}
                                        , {text: 'yyyy年', value: 'yyyy年'}
                                    ];
                                    break;
                                case 'month':
                                    options = [
                                        {text: 'yyyy-MM', value: 'yyyy-MM'}
                                        , {text: 'yyyy年MM月', value: 'yyyy年MM月'}
                                    ];
                                    break;
                                case 'time':
                                    options = [
                                        {text: 'HH:mm:ss', value: 'HH:mm:ss'}
                                        , {text: 'HH时mm分ss秒', value: 'HH时mm分ss秒'}
                                    ];
                                    break;
                                case 'date':
                                    options = [
                                        {text: 'yyyy-MM-dd', value: 'yyyy-MM-dd'}
                                        , {text: 'yyyy年MM月dd日', value: 'yyyy年MM月dd日'}
                                    ];
                                    break;
                                case 'datetime':
                                    options = [
                                        {text: 'yyyy-MM-dd HH:mm:ss', value: 'yyyy-MM-dd HH:mm:ss'}
                                        , {text: 'yyyy年MM月dd日 HH时mm分ss秒', value: 'yyyy年MM月dd日 HH时mm分ss秒'}
                                    ];
                                    break;
                            }

                            var isExist = false;
                            for (var item in options) {
                                if (item.value == json.dateformat) {
                                    isExist = true;
                                    break;
                                }
                            }

                            if (!isExist) {
                                json.dateformat = options[0].value;
                            }

                            currOption = json.dateformat;

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '<select name="{0}" lay-verify="required">'.format(key);
                            for (var i = 0; i < options.length; i++) {
                                if (options[i].value === currOption) {
                                    _html += '<option value="{0}" selected>{1}</option>'.format(options[i].value, options[i].text);
                                } else {
                                    _html += '<option value="{0}">{1}</option>'.format(options[i].value, options[i].text);
                                }
                            }
                            _html += '</select>';
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
                layui.form.render();
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        case 'radio-text':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['text'] = _value;
                            that.update(json);
                            break;
                        case 'radio-value':
                            var index = $(this).parents('.select-options').data('index');
                            json.options[index]['value'] = _value;
                            that.update(json);
                            break;
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //下拉框
                layui.form.on('select', function (data) {
                    var _key = data.elem.name;
                    var _value = data.value;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                            that.property(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [
                    'layui.laydate.render({'
                    , 'elem: "#' + item.tag + item.id + '",'
                    , 'type:"' + item.datetype + '",'
                    , 'format:"' + item.dateformat + '",'
                    , 'isInitValue:' + item.isInitValue + ''
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        explain: {
            name: '说明文字'
            , show: true
            , field: {
                id: '-1',
                field: "explain",
                tag: "explain",
                label: "说明文字",
                content: "说明文字：",
            }
            , lang: {
                field: "字段",
                label: "标签",
                content: "内容",
            }
            , render: function (json, pack, mask) {
                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<blockquote class="layui-elem-quote layui-quote-nm">{0}</blockquote>'.format(json.content);

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-elem-quote').remove();
                $('#' + json.id).append(this.render(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //文本域
                        case 'content':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <textarea name="{0}" placeholder="请输入内容" class="layui-textarea">{1}</textarea>'.format(key, json.content);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //文本域
                $(document).off('keyup', '#columnProperty .layui-textarea').on('keyup', '#columnProperty .layui-textarea', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [].join('');
            }
            , cssStr: function (item) {
                return [
                    '#formDesigner_1631063987 .layui-elem-quote {'
                    , 'margin-bottom:0;'
                    , '}'
                ].join('');
            }
        },
        images: {
            name: '多图上传'
            , show: true
            , field: {
                id: '-1',
                field: 'images',
                tag: "images",
                label: "多图上传",
                defaultValue: '',
                uploadUrl: '',
            }
            , lang: {
                field: "字段",
                label: "标签",
                uploadUrl: "上传地址",
            }
            , render: function (json, pack, mask) {
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;
                var imgs = [];
                if (_defaultValue) {
                    imgs = _defaultValue.split(',');
                }

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label">{0}：</label>'.format(json.label);

                _html += '<div class="layui-input-block">';

                _html += '<div class="layui-upload">';

                _html += '<button type="button" class="layui-btn" id="{0}">多图片上传</button>'.format(json.tag + '_' + json.id);

                _html += '<blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;width: 88%">预览图：';

                _html += '<div class="layui-upload-list uploader-list" style="overflow: auto;" id="uploader-list-{0}">'.format(json.id);

                for (var i = 0; i < imgs.length; i++) {
                    if (imgs[i]) {
                        _html += '<div class="file-item"><div class="handle"><i class="layui-icon layui-icon-delete"></i></div><img style="width: 100px;height: 100px;" src="' + imgs[i] + '"></div>';
                    }
                }

                _html += '</div>';

                _html += '</blockquote>';

                _html += '</div>';

                _html += '<input type="hidden" name="{0}" value="{1}">'.format(json.field, _defaultValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'uploadUrl':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [
                    'function ' + item.tag + '_' + item.id + '(){'
                    , 'var files = "";'
                    , 'layui.jquery("#uploader-list-' + item.id + '").find("img").each(function(i){'
                    , 'files = files+","+layui.jquery(this).attr("src");'
                    , '});'
                    , 'layui.jquery("#' + item.id + '").find("input[type=\'hidden\']").val(files);'
                    , '}'
                    , 'layui.upload.render({'
                    , 'elem: "#' + item.tag + '_' + item.id + '" '
                    , ', url: "' + item.uploadUrl + '"'
                    , ', multiple: true'
                    , ', done: function (res) {'
                    , 'var that = this;'
                    , 'if(res.code == 0){'
                    , 'layui.jquery("#uploader-list-' + item.id + '").append('
                    , '\'<div class="file-item"><div class="handle"><i class="layui-icon layui-icon-delete"></i></div><img style="width: 100px;height: 100px;" src="\'+ res.data.src + \'"></div>\''
                    , ');'
                    , item.tag + '_' + item.id + '();'
                    , '}'
                    , '}'
                    , '});'
                    , 'layui.jquery(document).off("click","#uploader-list-' + item.id + ' .layui-icon-delete").on("click","#uploader-list-' + item.id + ' .layui-icon-delete",function(){'
                    , 'layui.jquery(this).parent().parent().remove();'
                    , item.tag + '_' + item.id + '();'
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [
                    '#' + item.id + '{'
                    , 'margin-left:0;'
                    , '}'
                    , '#' + item.id + ' .info {'
                    , 'position:relative;'
                    , 'margin-top:-25px;'
                    , 'background-color:black;'
                    , 'color:white;'
                    , 'filter:alpha(Opacity=80);'
                    , '-moz-opacity:0.5;'
                    , 'opacity:0.5;'
                    , 'width:100px;'
                    , 'height:25px;'
                    , 'text-align:center;'
                    , 'display:none;'
                    , '}'
                    , '#' + item.id + ' .handle {'
                    , 'position:absolute;'
                    , 'background-color:black;'
                    , 'color:white;'
                    , 'filter:alpha(Opacity=80);'
                    , '-moz-opacity:0.5;'
                    , 'opacity:0.5;'
                    , 'width:100px;'
                    , 'text-align:right;'
                    , 'height:24px;'
                    , 'margin-bottom:-18px;'
                    , 'display:none;'
                    , '}'
                    , '#' + item.id + ' .handle i {'
                    , 'margin-right:5px;'
                    , 'cursor:pointer;'
                    , '}'
                    , '#' + item.id + ' .file-item:hover .handle {'
                    , 'display:block;'
                    , '}'
                    , '#' + item.id + ' .file-item {'
                    , 'margin:12px 0 0 15px;'
                    , 'padding:1px;'
                    , 'float:left;'
                    , 'position:relative;'
                    , '}'
                    , '#' + item.id + ' .layui-elem-quote {'
                    , 'margin-bottom:0;'
                    , '}'
                ].join('');
            }
        },
        files: {
            name: '多文件上传'
            , show: true
            , field: {
                id: '-1',
                field: 'files',
                tag: "files",
                label: "多文件上传",
                defaultValue: '',
                uploadUrl: '',
            }
            , lang: {
                field: "字段",
                label: "标签",
                uploadUrl: "上传地址",
            }
            , render: function (json, pack, mask) {
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;
                var files = [];
                if (_defaultValue) {
                    files = _defaultValue.split(',');
                }

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label">{0}：</label>'.format(json.label);

                _html += '<div class="layui-input-block">';

                _html += '<div class="layui-upload">';

                _html += '<button type="button" class="layui-btn layui-btn-normal" id="{0}">选择多文件</button> '.format(json.tag + '_' + json.id);

                _html += ' <div class="layui-upload-list" style="max-width: 1000px;"><table class="layui-table">';

                _html += '<colgroup><col><col width="150"><col width="260"><col width="150"></colgroup>';

                _html += '<thead><tr><th>文件名</th><th>大小</th><th>上传进度</th><th>操作</th></tr></thead>';

                _html += '<tbody id="list-{0}">'.format(json.id);

                for (var i = 0; i < files.length; i++) {
                    if (files[i]) {
                        _html += '<tr>';
                        _html += '<td>' + files[i] + '</td>';
                        _html += '<td></td>';
                        _html += '<td></td>';
                        _html += '<td><a href="' + files[i] + '" target="_blank" class="layui-btn layui-btn-xs demo-view">预览</a><button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button></td>';
                    }
                }

                _html += '</tbody></table></div>';

                _html += '</div>';

                _html += '<input type="hidden" name="{0}" value="{1}">'.format(json.field, _defaultValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'uploadUrl':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [
                    'function ' + item.tag + '_' + item.id + '(){'
                    , 'var files = "";'
                    , 'layui.jquery("#list-' + item.id + '").find("a").each(function(i){'
                    , 'files = files+","+layui.jquery(this).attr("href");'
                    , '});'
                    , 'layui.jquery("#' + item.id + '").find("input[type=\'hidden\']").val(files);'
                    , '}'
                    , 'layui.upload.render({'
                    , 'elem: "#' + item.tag + '_' + item.id + '" '
                    , ', url: "' + item.uploadUrl + '"'
                    , ' ,elemList: layui.jquery("#list-' + item.id + '")'
                    , ',field: "file"'
                    , ',accept: "file"'
                    , ',multiple: true'
                    , ',auto: true'
                    , ',bindAction: "#listAction-' + item.id + '"'
                    , ',choose: function(obj){'
                    , 'var that = this;'
                    , 'var files = this.files = obj.pushFile();'
                    , 'obj.preview(function(index, file, result){'
                    , 'var tr = layui.jquery([\'<tr id="upload-\'+ index +\'">\''
                    , ',"<td>"+ file.name +"</td>"'
                    , ',"<td>"+ (file.size/1014).toFixed(1) +"kb</td>"'
                    , ',\'<td><div class="layui-progress" lay-filter="progress-demo-\'+ index +\'"><div class="layui-progress-bar" lay-percent=""></div></div></td>\''
                    , ',\'<td>\',\'<a href="javascript:;" class="layui-btn layui-btn-xs demo-view">预览</a>\',,\'<button class="layui-btn layui-btn-xs layui-btn-normal demo-reload layui-hide">重传</button>\',\'<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>\',"</td>"'
                    , ',"</tr>"].join(""));'
                    , 'tr.find(".demo-reload").on("click", function(){obj.upload(index, file);});'
                    , 'tr.find(".demo-delete").on("click", function(){delete files[index];tr.remove();uploadListIns.config.elem.next()[0].value = ""; });'
                    , 'that.elemList.append(tr);'
                    , 'layui.element.render("progress");})}'
                    , ',done: function(res, index, upload){'
                    , 'var that = this;'
                    , 'if(res.code == 0){'
                    , 'var tr = that.elemList.find("tr#upload-"+ index);tr.find(".demo-reload").remove();tr.find(".demo-view").attr({href:res.data.src,target:"_blank"});tr.attr("src",res.data.src);delete this.files[index];'
                    , item.tag + '_' + item.id + '();'
                    , '}else{'
                    , 'that.error(index, upload);'
                    , '}'
                    , '}'
                    , ',allDone: function(obj){console.log(obj)}'
                    , ',error: function(index, upload){var that = this;var tr = that.elemList.find("tr#upload-"+ index),'
                    , 'tds = tr.children();tds.eq(3).find(".demo-reload").removeClass("layui-hide");}'
                    , ',progress: function(n, elem, e, index){layui.element.progress("progress-demo-"+ index, n + "%");}'
                    , '});'
                    , 'layui.jquery(document).off("click","#list-' + item.id + ' .demo-delete").on("click","#list-' + item.id + ' .demo-delete",function(){'
                    , 'layui.jquery(this).parents("tr").remove();'
                    , item.tag + '_' + item.id + '();'
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        editor: {
            name: '富文本编辑器'
            , show: true
            , field: {
                id: '-1',
                field: 'editor',
                label: "富文本编辑器",
                tag: "editor",
                defaultValue: "",
                uploadUrl: '',
            }
            , lang: {
                field: "字段",
                label: "标签",
                uploadUrl: "上传地址",
            }
            , render: function (json, pack, mask) {
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item layui-form-text" data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label">{0}：</label>'.format(json.label);

                _html += '<div class="layui-input-block">';

                _html += '<textarea id="{0}" name="{1}" style="display: none;">{2}</textarea>'.format(json.tag + '_' + json.id, json.field, _defaultValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                eval(this.scriptStr(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'uploadUrl':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [
                    'function ' + item.tag + '_' + item.id + '(iframeWin){'
                    , '$("#"+textareaId).val(iframeWin.document.body.innerHTML);'
                    , '}'
                    , 'var textareaId = "' + item.tag + '_' + item.id + '";'
                    , 'var layeditIndex = layui.layedit.build(textareaId,{'
                    , 'height:100,'
                    , 'uploadImage:{type:"post",url:"' + item.uploadUrl + '"}'
                    , '});'
                    , 'var iframeWin = $("#LAY_layedit_"+ layeditIndex).prop("contentWindow");'
                    , 'layui.jquery(iframeWin.document).off("DOMNodeInserted blur keyup paste copy cut mouseup","body").on("DOMNodeInserted blur keyup paste copy cut mouseup","body",function(){'
                    , item.tag + '_' + item.id + '(iframeWin);'
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        slider: {
            name: '滑块'
            , show: true
            , field: {
                id: '-1',
                field: "slider",
                tag: "slider",
                label: "滑块",
                defaultValue: '0',
                maxValue: '100',
                minValue: '0',
                stepValue: '1',
                isInput: true,
                disabled: false,
            }
            , lang: {
                field: "字段",
                label: "标签",
                maxValue: "最大值",
                minValue: "最小值",
                stepValue: "步进值",
                isInput: "手动输入",
                disabled: "禁用",
            }
            , render: function (json, pack, mask) {
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label">{0}：</label>'.format(json.label);

                _html += '<div class="layui-input-block layui-form">';

                _html += '<div id="{0}" class="widget-slider"></div>'.format(json.tag + '_' + json.id);

                _html += '<input name="{0}" type="hidden" value="{1}"></input>'.format(json.field, _defaultValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                eval(this.scriptStr(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'isInput':
                        case 'disabled':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'readonly':
                                    yes = "只读";
                                    no = "可写";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                var _defaultValue = item.val[item.field] ? item.val[item.field] : item.defaultValue;

                return [
                    'layui.slider.render({'
                    , 'elem: "#' + item.tag + '_' + item.id + '",'
                    , 'value: ' + _defaultValue + ','
                    , 'min: ' + item.minValue + ','
                    , 'max: ' + item.maxValue + ','
                    , 'step: ' + item.stepValue + ','
                    , 'disabled: ' + item.disabled + ','
                    , 'input: ' + item.isInput + ','
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [
                    '#' + item.tag + '_' + item.id + '{'
                    , 'margin: 18px 0;'
                    , 'width: 100%;'
                    , 'box-sizing: border-box;'
                    , 'padding: 0 15px;'
                    , 'position: absolute !important;'
                    , '}'
                ].join('');
            }
        },
        file: {
            name: '文件上传'
            , show: true
            , field: {
                id: '-1',
                field: "file",
                tag: "file",
                label: "文件上传",
                defaultValue: '',
                uploadBtn: '上传文件',
                uploadUrl: '',
                uploadExts: 'jpg|png|gif|bmp|jpeg',
            }
            , lang: {
                field: "字段",
                label: "标签",
                uploadBtn: "上传按钮",
                uploadUrl: '上传地址',
                uploadExts: '文件后缀',
            }
            , render: function (json, pack, mask) {
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label">{0}：</label>'.format(json.label);

                _html += '<div class="layui-input-block">';

                _html += '<div class="layui-upload">';

                _html += '<button type="button" class="layui-btn" id="{0}"><i class="layui-icon">&#xe67c;</i>{1}</button>'.format(json.tag + '_' + json.id, json.uploadBtn);

                _html += '<a href="{0}" target="_blank" class="layui-word-aux">{0}</a>'.format(_defaultValue);

                _html += '<input name="{0}" type="hidden" value="{1}" />'.format(json.field, _defaultValue);

                _html += '</div>';

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'uploadBtn':
                        case 'uploadExts':
                        case 'uploadUrl':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                return [
                    'layui.upload.render({'
                    , 'elem: "#' + item.tag + '_' + item.id + '"'
                    , ',url: "' + item.uploadUrl + '"'
                    , ',exts: "' + item.uploadExts + '"'
                    , ',field: "file"'
                    , ',accept: "file"'
                    , ',done: function(res){'
                    , 'layui.layer.msg(res.msg);'
                    , 'if(res.code==0){'
                    , 'console.log(layui.jquery(this.elem).parent().find(".layui-upload-img"));'
                    , 'layui.jquery(this.elem).parent().find(".layui-word-aux").attr("href",res.data.src).text(res.data.src);'
                    , 'layui.jquery(this.elem).parent().find("input[type=\'hidden\']").val(res.data.src);'
                    , '}'
                    , '}'
                    , ',error: function(){'
                    , 'layui.layer.msg("上传失败");'
                    , '}'
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [].join('');
            }
        },
        rate: {
            name: '评分'
            , show: true
            , field: {
                id: '-1',
                field: "rate",
                tag: "rate",
                label: "评分",
                defaultValue: '0',
                rateLength: '5',
                readonly: false,
            }
            , lang: {
                field: "字段",
                label: "标签",
                rateLength: "等级",
                readonly: "只读",
            }
            , render: function (json, pack, mask) {
                var _defaultValue = json.val[json.field] ? json.val[json.field] : json.defaultValue;

                var _html = '';

                if (pack) {
                    _html += '<div id="{0}" class="layui-form-item"  data-id="{0}" data-tag="{1}" data-index="{2}">'.format(json.id, json.tag, json.index);
                }

                _html += '<label class="layui-form-label">{0}：</label>'.format(json.label);

                _html += '<div class="layui-input-block">';

                _html += '<div id="{0}"></div>'.format(json.tag + '_' + json.id);

                _html += '<input name="{0}" type="hidden" value="{1}"></input>'.format(json.field, _defaultValue);

                _html += '</div>';

                if (mask) {
                    _html += '<div class="widget-view-mask"></div>';
                }

                if (pack) {
                    _html += '</div>';
                }

                return _html;
            }
            , update: function (json) {
                $('#' + json.id + ' .layui-input-block').remove();
                $('#' + json.id + ' .layui-form-label').remove();
                $('#' + json.id).append(this.render(json));
                eval(this.scriptStr(json));
            }
            , jsonData: function (id) {
                var _json = JSON.parse(JSON.stringify(this.field));
                _json.id = id;
                return _json;
            }
            , property: function (json) {
                var _html = '';
                var lang = this.lang;

                for (var key in json) {
                    switch (key) {
                        //输入框
                        case 'field':
                        case 'label':
                        case 'rateLength':
                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="text" id="{0}" name="{0}" value="{1}" lay-verify="required" placeholder="请输入{2}" autocomplete="off" class="layui-input">'.format(key, json[key] ? json[key] : '', lang[key]);
                            _html += '  </div>';
                            _html += '</div>';
                            break;

                        //开关
                        case 'readonly':
                            var yes = "";
                            var no = "";

                            switch (key) {
                                case 'readonly':
                                    yes = "只读";
                                    no = "可写";
                                    break;
                                case 'required':
                                    yes = "必填";
                                    no = "可选";
                                    break;
                                default:
                                    yes = "是";
                                    no = "否";
                            }

                            _html += '<div class="layui-form-item" >';
                            _html += '  <label class="layui-form-label">{0}</label>'.format(lang[key]);
                            _html += '  <div class="layui-input-block">';
                            _html += '    <input type="checkbox" id="{1}" {0} name="{1}" lay-skin="switch" lay-text="{2}|{3}">'.format(json[key] ? 'checked' : '', key, yes, no);
                            _html += '  </div>';
                            _html += '</div>';
                            break;
                    }
                }

                $('#columnProperty').empty().html(_html);
            }
            , bindEvent: function (json) {
                var that = this;

                //输入框
                $(document).off('keyup', '#columnProperty .layui-input').on('keyup', '#columnProperty .layui-input', function () {
                    var _key = $(this).attr("name");
                    var _value = $(this).val();

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });

                //开关
                layui.form.on('switch', function (data) {
                    var _key = data.elem.name;
                    var _value = data.elem.checked ? true : false;

                    switch (_key) {
                        default:
                            json[_key] = _value;
                            that.update(json);
                    }
                });
            }
            , scriptStr: function (item) {
                var _defaultValue = item.val[item.field] ? item.val[item.field] : item.defaultValue;

                return [
                    'layui.rate.render({'
                    , 'elem: "#' + item.tag + '_' + item.id + '",'
                    , 'length: ' + item.rateLength + ','
                    , 'value: ' + _defaultValue + ','
                    , 'readonly: ' + item.readonly + ','
                    , 'choose: function(value){'
                    , 'layui.jquery("#' + item.id + '").find("input").val(value);'
                    , '}'
                    , '});'
                ].join('');
            }
            , cssStr: function (item) {
                return [
                    '#' + item.tag + '_' + item.id + '{'
                    , 'margin-bottom: 0;'
                    , '}'
                    , '#' + item.tag + '_' + item.id + ' .layui-rate{'
                    , 'padding: 5px;'
                    , 'padding-left: 15px;'
                    , '}'
                ].join('');
            }
        },
    };

    //组件分组
    Class.prototype.componentGroups = [
        {
            name: "基础组件",
            show: true,
            list: [
                'input',
                'textarea',
                'select',
                'switch',
                'radio',
                'checkbox',
                'date',
                'explain',
            ]
        }
        , {
            name: "扩展组件",
            show: true,
            list: [
                'slider',
                'rate',
                'file',
                'files',
                'images',
                'editor',
            ]
        }
    ];

    //渲染视图
    Class.prototype.render = function () {
        var that = this
            , options = that.config
            , top = options.showHeader ? 60 : 0;

        if (!options.elem) {
            return;
        }

        options.elem = $(options.elem);

        options.firstRender = true;

        var _listhtml = "";

        for (var i = 0; i < that.componentGroups.length; i++) {
            if (that.componentGroups[i].show) {
                _listhtml += '<div class="components-title">{0} </div>'.format(that.componentGroups[i].name);
                _listhtml += '<div class="components-draggable" id="c';
                _listhtml += i;
                _listhtml += '">';

                $.each(that.componentGroups[i].list, function (index, item) {
                    if (that.components[item] && that.components[item].show) {
                        _listhtml += '<div class="components-item" data-tag="{0}"><div class="components-body"><i class="icon iconfont icon-input"></i>{1}</div></div>'.format(item, that.components[item].name);
                    }
                });

                _listhtml += '</div>';
            }
        }

        options.elem.html(TP_MAIN.format(top));
        $('#components-form-list').append(_listhtml);
        $('body').append(TP_HTML_VIEW);
        $('body').append(TP_IMPORT_VIEW);

        //排序事件注册
        for (var j = 0; j < that.componentGroups.length; j++) {
            if (that.componentGroups[j].show) {
                layui.Sortable.create(document.getElementById("c" + j), {
                    group: {
                        name: 'formgroup',
                        pull: 'clone',
                        put: false,
                    },
                    sort: false,
                    animation: 150
                });
            }
        }

        //表单创建、排序事件
        layui.Sortable.create(document.getElementById("formDesignerForm"), {
            group: {
                name: 'formgroup'
            },
            handle: '.widget-view-drag',
            ghostClass: "ghost",
            animation: 200,
            onAdd: function (evt) {
                if (options.data === undefined) {
                    return;
                }

                if (typeof (options.data) === 'string') {
                    options.data = JSON.parse(options.data);
                }

                //注意这里的一个bug，newIndex 第一次拖动也是1 第二次拖动也是1
                if (options.data.length === 0) {
                    evt.newIndex = 0;
                }

                //添加组件数据
                var _id = that.autoId(evt.item.dataset.tag);
                var _newitem = that.components[evt.item.dataset.tag].jsonData(_id);
                options.selectItem = _newitem;
                options.data.splice(evt.newIndex, 0, _newitem);

                that.renderForm();
            },
            onEnd: function (evt) {
                //只有当to的目标容器是 formDesignerForm才出发次逻辑
                if (evt.to.id === 'formDesignerForm') {
                    that.moveItem(evt.oldIndex, evt.newIndex);
                }
            }
        });

        //导出数据
        $('.exportJson').on('click', function () {
            document.getElementById('generate-code-view').value = JSON.stringify(options.data, null, 4);

            layui.layer.open({
                type: 1
                , title: 'JSON 数据导出'
                , id: 'Lay_layer_htmlcodeview'
                , content: $('.htmlcodeview')
                , area: ['800px', '640px']
                , resize: false
                , shadeClose: true
                , end: function () {
                    $('.htmlcodeview').css("display", "none")
                }
            });
        });
        $('#copy-html-code').on('click', function () {
            var Url2 = document.getElementById("generate-code-view");
            Url2.select();
            document.execCommand("Copy");
            layui.layer.msg('复制成功');
        });

        //导入数据
        $('.importJson').on('click', function () {
            document.getElementById('import-json-code').value = JSON.stringify(options.data, null, 4);

            layui.layer.open({
                type: 1
                , title: 'JSON模板数据导入'
                , id: 'Lay_layer_importjsoncodeview'
                , content: $('.importjsoncodeview')
                , area: ['800px', '640px']
                , resize: false
                , shadeClose: true
                , end: function () {
                    $('.importjsoncodeview').css("display", "none")
                }
            });
        });
        $('#import-json-code').on('click', function () {
            var _value = document.getElementById("import-json-code-view").value;
            if (_value) {
                _value = JSON.parse(_value);
                options.selectItem = _value[0];
                options.data = _value;
                that.renderForm();
                layui.layer.closeAll();
                layui.layer.msg('导入成功');
            }
        });

        //生成代码
        $('.generateCode').on('click', function () {
            that.generateComponentCode(options.data);

            var TP_HTML_CODE = [
                '<!DOCTYPE html>'
                , '<html>'
                , '<head>'
                , '<meta charset="utf-8">'
                , '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">'
                , '<title>表单设计器代码</title>'
                , '<link rel="stylesheet" href="' + layuiPath(true, true) + '/css/layui.css" />'
                , '<style>' + options.componentCode.css + '</style>'
                , '</head>'
                , '<body>'
                , '<div id="testdemo" style="margin: 10px 20px;"><form  class="layui-form" style="height:100%;" id="formPreviewForm">'
                , '' + options.componentCode.html + ''
                , '<div class="layui-form-item">'
                , '<div class="layui-input-block">'
                , '<button type="submit" class="layui-btn" lay-submit="" lay-filter="formPreviewForm">提交</button>'
                , '<button type="reset" class="layui-btn layui-btn-primary">重置</button>'
                , '</div>'
                , '</div>'
                , '</form></div>'
                , '<script type="text/javascript" src="' + layuiPath(false, true) + '"></script>'
                , '<script>'
                , 'layui.config({base: "'
                , layui.cache.base
                , '"}).use(' + JSON.stringify(modules) + ', function () {'
                , 'var $ = layui.jquery;'
                , options.componentCode.script
                , '});'
                , '</script>'
                , '</body>'
                , '</html>'
            ].join('');

            var tabsize = 4;
            var tabchar = ' ';
            if (tabsize == 1) {
                tabchar = '\t';
            }

            document.getElementById('generate-code-view').value = style_html(TP_HTML_CODE, tabsize, tabchar, 400);

            layui.layer.open({
                type: 1
                , title: 'HTML代码'
                , id: 'Lay_layer_htmlcodeview'
                , content: $('.htmlcodeview')
                , area: ['800px', '640px']
                , resize: false
                , shadeClose: true
                , end: function () {
                    $('.htmlcodeview').css("display", "none")
                }
            });

        });

        //预览效果
        $('.previewForm').on('click', function () {
            window.localStorage.setItem('layui_form_json', JSON.stringify(options.data));
            window.open(layui.cache.base + '../view.html', '_blank')
        });

        //渲染表单
        that.renderForm();
    };

    //渲染表单视图
    Class.prototype.renderForm = function () {
        var that = this
            , options = that.config;
        var elem = $('#formDesignerForm');

        //清空
        elem.empty();

        //渲染表单组件
        that.renderComponent(options.data, elem);

        //选中当前组件
        if (options.selectItem) {
            that.selectCurrItem();
        }

        //添加点击事件
        that.addClick();

        layui.form.render();
    };

    //渲染表单组件
    Class.prototype.renderComponent = function (jsondata, elem) {
        var that = this
            , options = that.config;

        that.generateComponentCode(jsondata, true);

        elem.append(options.componentCode.html);
        styleInject(options.componentCode.css, elem[0]);
        eval(options.componentCode.script);

        if (typeof options.callback == 'function') {
            options.callback(jsondata);
        }
    };

    //生成组件代码
    Class.prototype.generateComponentCode = function (jsondata, mask) {
        var that = this
            , options = that.config;

        options.componentCode.html = '';
        options.componentCode.css = '';
        options.componentCode.script = '';

        $.each(jsondata, function (index, item) {
            item.val = options.val;
            options.componentCode.html += that.components[item.tag].render(item, true, mask);
            options.componentCode.css += that.components[item.tag].cssStr(item);
            options.componentCode.script += that.components[item.tag].scriptStr(item);

            if (options.firstRender && index == 0) {
                options.selectItem = that.findJsonItem(jsondata, item.id);
                options.firstRender = false;
            }
        });
    };

    //绑定点击事件
    Class.prototype.addClick = function () {
        var that = this
            , options = that.config;

        $("#formDesignerForm .layui-form-item").on('click', function (e) {
            e.preventDefault();
            e.stopPropagation();

            var _id = $(this)[0].dataset.id;
            var tag = $(this)[0].dataset.tag;

            //切换当前选中
            options.selectItem = that.findJsonItem(options.data, _id);

            //选中当前组件
            that.selectCurrItem();

            layui.form.render();
        });
    };

    //选中当前组件
    Class.prototype.selectCurrItem = function () {
        var that = this
            , options = that.config;

        var _draghtml = '<div class="widget-view-action"  id="widget-view-action"><i class="layui-icon layui-icon-file"></i><i class="layui-icon layui-icon-delete"></i></div><div class="widget-view-drag" id="widget-view-drag"><i class="layui-icon layui-icon-screen-full"></i></div>';

        $('#widget-view-action').remove();
        $('#widget-view-drag').remove();
        $('#' + options.selectItem.id).append(_draghtml);

        //给当前元素增加class
        $('#formDesignerForm .layui-form-item').removeClass('active');
        $('#' + options.selectItem.id).addClass('active');

        //属性渲染
        that.components[options.selectItem.tag].property(options.selectItem);

        //属性绑定
        that.components[options.selectItem.tag].bindEvent(options.selectItem);

        //复制和删除事件
        that.addCopyDeleteClick();
    };

    //移动视图
    Class.prototype.moveItem = function (oldIndex, newIndex) {
        var that = this
            , options = that.config;
        var newData = options.data[newIndex];
        var oldData = options.data[oldIndex];
        options.data[newIndex] = oldData;
        options.data[oldIndex] = newData;
    };

    //自动生成ID
    Class.prototype.autoId = function (tag) {
        var d = new Date().getTime();
        return tag + '_' + d;
    };

    //获取组件数据
    Class.prototype.findJsonItem = function (json, id) {
        for (var i = 0; i < json.length; i++) {
            if (json[i].id === id) {
                return json[i];
            }
        }
        return undefined;
    };

    //删除组件数据
    Class.prototype.deleteJsonItem = function (json, id) {
        var index = 0;
        for (var i = 0; i < json.length; i++) {
            if (json[i].id === id) {
                json.splice(i, 1);
                index = i;
                break;
            }
        }
        return json.length ? (index ? json[index - 1] : json[0]) : undefined;
    };

    //复制和删除事件
    Class.prototype.addCopyDeleteClick = function () {
        var that = this
            , options = that.config;

        if (options.data === undefined) {
            return;
        }
        if (typeof (options.data) === 'string') {
            options.data = JSON.parse(options.data);
        }

        //复制
        $('#formDesignerForm .layui-form-item .widget-view-action .layui-icon-file').on('click', function (e) {
            e.stopPropagation();

            if (options.data === undefined) {
                return;
            }
            if (typeof (options.data) === 'string') {
                options.data = JSON.parse(options.data);
            }

            var _id = document.elementFromPoint(e.pageX, e.pageY).parentElement.parentElement.dataset.id;
            if (_id !== undefined) {
                options.selectItem = that.copyJsonAfterItem(options.data, _id);
            }

            that.renderForm();
        });

        //删除
        $('#formDesignerForm .layui-form-item .layui-icon-delete').on('click', function (e) {
            e.stopPropagation();

            var _id = document.elementFromPoint(e.pageX, e.pageY).parentElement.parentElement.dataset.id;
            if (_id !== undefined) {
                options.selectItem = that.deleteJsonItem(options.data, _id);
            }

            that.renderForm();
        });
    };

    //复制组件
    Class.prototype.copyJsonAfterItem = function (json, id) {
        var that = this;
        for (var i = 0; i < json.length; i++) {
            if (json[i].id === id) {
                var _newjson = JSON.parse(JSON.stringify(json[i]));
                _newjson.id = that.autoId(_newjson.tag);
                json.splice(i + 1, 0, _newjson);
                return json[i + 1];
            }
        }
        return undefined;
    };

    //实例
    var formDesigner = {
        //渲染操作视图
        render: function (options) {
            var that = new Class(options);
            that.render();
            return that;
        }
        //渲染组件视图
        , renderComponent: function (elem, jsondata, values) {
            var that = new Class({val: values});
            that.renderComponent(jsondata, $(elem));
            return that;
        }
    };

    exports(MOD_NAME, formDesigner);
});